class Vue{
    constructor(options) {
        this.$el = options.el;
        this.$data = options.data;
        if(this.$el){
            //数据劫持
            new Observer(this.$data);

            //模板编译 初始化视图
            new Compile(this.$el,this);
        }
    } 
}

//被观察者
class Dep{
    constructor(){
        this.watchers = [];
    }
    add(watcher){
        this.watchers.push(watcher);
    }
    notify(){
        this.watchers.forEach(watcher => {
            watcher.update();
        })
    }
}

//观察者
class Watcher{
    constructor(vm,expr,cb){
        this.vm = vm;
        this.expr = expr;
        this.cb = cb;
        //旧值
        this.oldValue = this.getVal(this.expr,this.vm)
    }

    getVal(expr,vm){
        Dep.target = this; //watcher的实例
        let value = compileUtil.getVal(expr,vm);
        Dep.target = null;
        return value
    }

    //监听到数据变化，更新视图
    update(){
        let newVal = compileUtil.getVal(this.expr,this.vm);
        if(newVal != this.oldValue){
            //数据发生变化
            this.cb(newVal);
        }
    }
}

//vm.$watch(vm,'user.username',(newValue) => {})

//数据劫持
class Observer{
    constructor(data){
        this.observer(data);
    }

    observer(data){
        // Object.defineProperty()
        if(data && typeof data === 'object'){
            for(let key in data){
                this.ObserverData(data,key,data[key]);
            }
        }
    }

    ObserverData(data,key,value){
        this.observer(value);
        let dep = new Dep();
        Object.defineProperty(data,key,{
            set(newValue){
                console.log('设置值')
                if(newValue != value){
                    value = newValue;
                    //通知观察者更新视图
                    dep.notify();
                }
            },
            get(){
                console.log("获取值");
                //添加观察者
                Dep.target && dep.add(Dep.target)
                return value
            }
        })
    }
}

class Compile{
    constructor(el,vm){
        this.vm = vm;
        this.el = this.isElementNode(el) ? el : document.querySelector(el);
        //把app内的子节点放到文档片段内
        let fragment = this.nodeToFragment(this.el);
        //进行编译
        this.compile(fragment);
        //把编译好的文档片段一次性添加到app节点内
        this.el.appendChild(fragment);
    }

    //编译
    compile(node){
        //区分两种情况：1.文本节点  2.元素节点
        let childNodes = node.childNodes; //只获取第一级的子节点

        [...childNodes].forEach(child => {
            if(this.isElementNode(child)){
                //元素节点
                this.compileElement(child);
                this.compile(child);
            }else{
                //文本节点
                this.compileText(child)
            }
        })
    }

    //是否为指令
    isDirective(attr){
        return attr.startsWith('v-')
    }

    //编译元素
    compileElement(node){
        let attrs = node.attributes;
        [...attrs].forEach(attr => {
            console.dir(attr)
            let {name,value:expr} = attr;
            if(this.isDirective(name)){
                //指令  v-html v-text v-model
                let directive = name.split('-')[1]; ////[v,html]  [v,model]
                //node节点  表达式  vue的实例
                compileUtil[directive](node,expr,this.vm)
            }
        })
    }

    //编译文本
    compileText(node){
        let content = node.textContent;
        if(/\{\{(.+?)\}\}/.test(content)){
            //根据表达式在data内查找对应的值，给节点赋值
            compileUtil.content(node,content,this.vm);
        }
    }

    nodeToFragment(node){
        //获取所有的子节点
        let childnodes = node.childNodes;
        //创建一个文档片段
        let fragment = document.createDocumentFragment();

        [...childnodes].forEach(child => {
            fragment.appendChild(child);
        })

        console.log(fragment)
        return fragment
    }

    //判断当前节点是否为元素节点
    isElementNode(node){
        return node.nodeType === 1
    }
}

compileUtil = {
    //获取值  expr :[ user,username]  
    getVal(expr,vm){
        return expr.split('.').reduce((pre,cur) => {
            return pre[cur]
        },vm.$data)
    },
    //设置值
    setVal(expr,vm,value){
        //[user,username]
        /**
         * {
                user:{
                    username:'zs',
                    age:18,
                },
                msg:'hello'
            }
         * 
        */
       console.log(expr)
        expr.split('.').reduce((pre,cur,index,arr) => {
            if(index === arr.length-1){
                return pre[cur] = value
            }
            return pre[cur]
        },vm.$data)
    },
    model(node,expr,vm){
        let value = this.getVal(expr,vm);
        let fn = this.updater.compileModel;
        fn(node,value);
        //实例化观察者
        new Watcher(vm,expr,(newValue) => {
            //更新视图
            fn(node,newValue);
        })
        
        //视图更改之后，驱动数据变化
        node.addEventListener('input',(e) => {
            //vm.$data.user.username = e.target.value
            this.setVal(expr,vm,e.target.value)
        })
    },
    content(node,content,vm){
        let fn = this.updater.compileContent;
        let value = content.replace(/\{\{(.+?)\}\}/g,(...argv) => {
            console.log(argv)
            //实例化观察者
            new Watcher(vm,argv[1],newValue => {
                fn(node,newValue)
            })
            return this.getVal(argv[1],vm);
        })
        fn(node,value)
        
    },
    updater:{
        compileModel(node,value){
            node.value = value
        },
        compileContent(node,value){
            node.textContent = value
        }
    }
}